/*
Copyright (c) 2009, Luke Berndt
All rights reserved.
 
 * More Information: http://www.tagflo.com

Redistribution and use in source and binary forms, with or without modification, are permitted 
provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this list of conditions 
  and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice, this list of conditions 
  and the following disclaimer in the documentation and/or other materials provided with the distribution.

* Neither the name of RoboTastic Software nor the names of its contributors may be used to endorse or 
  promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
POSSIBILITY OF SUCH DAMAGE.
*/


using System.Windows.Media.Imaging;
using System.Windows.Media;
using System;
using System.Diagnostics;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.IO;

namespace TagFlo
{

    public class PhotoFile
    {

        private string _path;

        private Uri _source;
        public string Source { get { return _path; } }
        public BitmapImage thumbnail;
        public BitmapImage Thumbnail { get { return thumbnail; } }
        public double width;
        public double height;
        //private Image _image;

        public BitmapFrame _image;


        public byte[] ThumbnailByteArray()
        {
      /*      byte[] thumbArray = new byte()[thumbnail.le];

            //MemoryStream stream = new MemoryStream();
            thumbnail.
            thumbnail.Save(stream, System.Drawing.Imaging.ImageFormat.Jpeg);
            return stream.ToArray();
            */



            byte[] result = null;

            BitmapEncoder encoder = new JpegBitmapEncoder();

           
                MemoryStream stream = new MemoryStream();

                encoder.Frames.Add(BitmapFrame.Create(thumbnail as BitmapSource));
                encoder.Save(stream);

                stream.Seek(0, SeekOrigin.Begin);

                result = new byte[stream.Length];

                BinaryReader br = new BinaryReader(stream);

                br.Read(result, 0, (int)stream.Length);

                br.Close();

                stream.Close();

            

            return result;


        }

        public static void RotateJPEG(string path, Rotation rotation)
{
    BitmapCreateOptions createOptions = BitmapCreateOptions.PreservePixelFormat | BitmapCreateOptions.IgnoreColorProfile;
 

    try
    {
        using (FileStream stream = new
        FileStream(System.IO.Path.GetDirectoryName(path) + @"\temp.jpg", FileMode.Create)) // FileMode.Create))
        {
            using (Stream file = File.Open(path, FileMode.Open, FileAccess.Read))
            {
                BitmapDecoder decoder = BitmapDecoder.Create(file, createOptions, BitmapCacheOption.None);

                //BitmapDecoder decoder = BitmapDecoder.Create(new Uri(path), BitmapCreateOptions.PreservePixelFormat | BitmapCreateOptions.IgnoreColorProfile, BitmapCacheOption.None);

                JpegBitmapEncoder encoder = new JpegBitmapEncoder();

                encoder.Rotation = rotation;
                encoder.Frames = decoder.Frames;
                encoder.Save(stream);
                stream.Flush();
            }
        }
        
        File.Delete(path);
        File.Move(System.IO.Path.GetDirectoryName(path) + @"\temp.jpg", path);
 
    }
    catch (Exception e)
    {
         string s = e.Message;
    
    }
}

        private static void RotateImage(string filename, Rotation rotate)
{
    BitmapCreateOptions createOptions = BitmapCreateOptions.PreservePixelFormat | BitmapCreateOptions.IgnoreColorProfile;
 
    using (Stream file = File.Open(filename, FileMode.Open, FileAccess.Read))
    {
         BitmapDecoder original = BitmapDecoder.Create(file, createOptions, BitmapCacheOption.None);
        JpegBitmapEncoder output = new JpegBitmapEncoder();
        output.Rotation = rotate;

     
       
      
        
        output.Frames.Add((BitmapFrame) original.Frames[0].Clone()); //.Add(BitmapFrame.Create(original.Frames[0], original.Frames[0].Thumbnail,metadata, original.Frames[0].ColorContexts));
        //output.Frames = original.Frames;  
        file.Close();  // close the file to ready for overwrite
        
        // finally, save the new file over the old file
        using (Stream outputFile = File.Open(filename, FileMode.OpenOrCreate, FileAccess.Write))
        {
            output.Save(outputFile);
            outputFile.Flush();
        }
    }
}



        private void BuildPhoto(string path)
        {
            _path = path;
            _source = new Uri(path);

            byte[] buffer = File.ReadAllBytes(path);
            MemoryStream mem = new MemoryStream(buffer);

            int thumb_width = 200;
            int thumb_height = 200;

            _image = BitmapFrame.Create(mem);
            thumbnail = new BitmapImage();
            thumbnail.BeginInit();

            width = _image.Width;
            height = _image.Height;


            if (_image.Width > _image.Height)
            {
                thumbnail.DecodePixelWidth = thumb_width;
            }
            else
            {
                thumbnail.DecodePixelHeight = thumb_height;
            }
            mem.Seek(0, SeekOrigin.Begin);

            thumbnail.StreamSource = mem;

            thumbnail.CreateOptions = BitmapCreateOptions.None;

            thumbnail.CacheOption = BitmapCacheOption.Default;

            RenderOptions.SetBitmapScalingMode(thumbnail, BitmapScalingMode.NearestNeighbor);

            thumbnail.EndInit();
            thumbnail.Freeze();






            try
            {
                _metadata = new ExifMetadata(_image);
            }

            catch (Exception ex)
            {
                Trace.WriteLine("Error creating ExifMetadata: " + ex.Message);

            }

        }

         public PhotoFile(string path, Rotation rotation)
         {
            
             if (rotation != 0)
             {
                 RotateJPEG(path, rotation);
                 //RotateImage(path, rotation);
             }
             BuildPhoto(path);
         }
             

        public PhotoFile(string path)
        {
            BuildPhoto(path);
        }

        public override string ToString()
        {
            return _source.ToString();
        }

        //private BitmapFrame _image;
        // public BitmapFrame Image { get { return _image; } set { _image = value; } }
        private ExifMetadata _metadata;
        public ExifMetadata Metadata { get { return _metadata; } }

    }

    /// <summary>
    /// This class represents a collection of photos in a directory.
    /// </summary>

    public enum ColorRepresentation
    {
        sRGB,
        Uncalibrated
    }

    public enum FlashMode
    {
        FlashFired,
        FlashDidNotFire
    }

    public enum ExposureMode
    {
        Manual,
        NormalProgram,
        AperturePriority,
        ShutterPriority,
        LowSpeedMode,
        HighSpeedMode,
        PortraitMode,
        LandscapeMode,
        Unknown
    }

    public enum WhiteBalanceMode
    {
        Daylight,
        Fluorescent,
        Tungsten,
        Flash,
        StandardLightA,
        StandardLightB,
        StandardLightC,
        D55,
        D65,
        D75,
        Other,
        Unknown
    }



    public class ExifMetadata
    {
        BitmapMetadata _metadata;


        public string Comment
        {
            get
            {
                string val = null;
                try
                {
                    val = _metadata.Comment;
                }
                catch (Exception ex)
                {
                    Trace.WriteLine("Error getting MetaData, Comment: " + ex.ToString());
                }
                return (val != null ? (string)val : String.Empty);
            }
        }

        public string Title
        {
            get
            {
                string val = null;
                try
                {
                    val = _metadata.Title;
                }
                catch (Exception ex)
                {
                    Trace.WriteLine("Error getting MetaData, Title: " + ex.ToString());
                }
                return (val != null ? (string)val : String.Empty);
            }
        }


        public int Rating
        {
            get
            {
                int val = -1;
                try
                {
                    val = _metadata.Rating;
                }
                catch (Exception ex)
                {
                    Trace.WriteLine("Error getting MetaData, Rating: " + ex.ToString());
                }

                return val;
            }
        }


        public string Subject
        {
            get
            {
                string val = null;
                try
                {
                    val = _metadata.Subject;
                }
                catch (Exception ex)
                {
                    Trace.WriteLine("Error getting MetaData, Subject: " + ex.ToString());
                }
                return (val != null ? (string)val : String.Empty);
            }
        }

        public string Location
        {
            get
            {
                string val = null;
                try
                {
                    val = _metadata.Location;
                }
                catch (Exception ex)
                {
                    Trace.WriteLine("Error getting MetaData, Location: " + ex.ToString());
                }
                return (val != null ? (string)val : String.Empty);
            }
        }

        public ReadOnlyCollection<string> TagList
        {
            get
            {
                ReadOnlyCollection<string> tagsList = null;
                try
                {
                    tagsList = _metadata.Keywords;
                }
                catch (Exception ex)
                {
                    Trace.WriteLine("Error getting MetaData, Comment: " + ex.ToString());
                }
                return tagsList;
            }

        }

        public String Tags
        {
            get
            {
                ReadOnlyCollection<string> tagsList = _metadata.Keywords;
                string val = "";
                try
                {
                    if (tagsList != null)
                    {
                        foreach (string tag in tagsList)
                        {
                            val = val + tag + ", ";
                        }
                    }
                }
                catch (Exception ex)
                {
                    Trace.WriteLine("Error generate MetaData.Tags: " + ex.Message);
                }
                return (val != null ? (string)val : String.Empty);
            }
        }

        public ExifMetadata(BitmapFrame _frame)
        {

            _metadata = (BitmapMetadata)_frame.Metadata;


        }

        private decimal ParseUnsignedRational(ulong exifValue)
        {
            decimal val = -1;
            try
            {
                val = (decimal)(exifValue & 0xFFFFFFFFL) / (decimal)((exifValue & 0xFFFFFFFF00000000L) >> 32);
            }
            catch (Exception ex)
            {
                Trace.WriteLine("Error with ParseUnsignedRational, value: " + exifValue + " Error: " + ex.ToString());
            }
            return val;
        }

        private decimal ParseSignedRational(long exifValue)
        {
            decimal val = -1;
            try
            {
                val = (decimal)(exifValue & 0xFFFFFFFFL) / (decimal)((exifValue & 0x7FFFFFFF00000000L) >> 32);
            }
            catch (Exception ex)
            {
                Trace.WriteLine("Error with ParseSignedRational, value: " + exifValue + " Error: " + ex.ToString());
            }

            return val;
        }

        private object QueryMetadata(string query)
        {
            if (_metadata.ContainsQuery(query))

                return _metadata.GetQuery(query);
            else
            {
                Trace.WriteLine("Error querying MetaData: " + query);
                return null;
            }
        }

        public uint? Width
        {
            get
            {

                object val = QueryMetadata("/app1/ifd/exif/subifd:{uint=40962}");
                if (val == null)
                {
                    return 0;
                }
                else
                {
                    if (val.GetType() == typeof(UInt32))
                    {
                        return (uint?)val;
                    }
                    else
                    {
                        return System.Convert.ToUInt32(val);
                    }
                }
            }
        }

        public uint? Height
        {
            get
            {
                object val = QueryMetadata("/app1/ifd/exif/subifd:{uint=40963}");
                if (val == null)
                {
                    return 0;
                }
                else
                {
                    if (val.GetType() == typeof(UInt32))
                    {
                        return (uint?)val;
                    }
                    else
                    {
                        return System.Convert.ToUInt32(val);
                    }
                }
            }
        }

        public decimal? HorizontalResolution
        {
            get
            {
                object val = QueryMetadata("/app1/ifd/exif:{uint=282}");
                if (val != null)
                {
                    return ParseUnsignedRational((ulong)val);
                }
                else
                {
                    return -1;
                }
            }
        }

        public decimal? VerticalResolution
        {
            get
            {
                object val = QueryMetadata("/app1/ifd/exif:{uint=283}");
                if (val != null)
                {
                    return ParseUnsignedRational((ulong)val);
                }
                else
                {
                    return -1;
                }
            }
        }

        public string CameraManufacturer
        {
            get
            {

                string val = null;
                try
                {
                    val = _metadata.CameraManufacturer;
                }
                catch (Exception ex)
                {
                    Trace.WriteLine("Error getting MetaData, CameraManufacturer: " + ex.ToString());
                }

                return (val != null ? (string)val : String.Empty);
            }
        }

        public string CameraModel
        {
            get
            {
                string val = null;
                try
                {
                    val = _metadata.CameraModel;
                }
                catch (Exception ex)
                {
                    Trace.WriteLine("Error getting MetaData, CameraModel: " + ex.ToString());
                }
                return (val != null ? (string)val : String.Empty);
            }
        }

        public string ApplicationName
        {
            get
            {
                string val = null;
                try
                {
                    val = _metadata.ApplicationName;
                }
                catch (Exception ex)
                {
                    Trace.WriteLine("Error getting MetaData, ApplicationName: " + ex.ToString());
                }
                return (val != null ? (string)val : String.Empty);
            }
        }

        public ColorRepresentation ColorRepresentation
        {
            get
            {
                if ((ushort)QueryMetadata("/app1/ifd/exif/subifd:{uint=40961}") == 1)
                    return ColorRepresentation.sRGB;
                else
                    return ColorRepresentation.Uncalibrated;
            }
        }

        public long ExposureTime
        {
            get
            {
                object val = QueryMetadata("/app1/ifd/exif/subifd:{uint=33434}");
                if (val == null)
                {
                    return -1;
                }
                try
                {
                    decimal exposure = ParseUnsignedRational((ulong)val);
                    exposure = Decimal.Round(1 / exposure);
                    return (uint)exposure;
                }
                catch (NullReferenceException)
                {
                    return -1;
                }

            }
        }

        public decimal? ExposureCompensation
        {
            get
            {
                object val = QueryMetadata("/app1/ifd/exif/subifd:{uint=37380}");
                if (val != null)
                {
                    return ParseSignedRational((long)val);
                }
                else
                {
                    return -1;
                }
            }
        }

        public long LensAperture
        {
            get
            {
                object val = QueryMetadata("/app1/ifd/exif/subifd:{uint=33437}");
                if (val != null)
                {
                    return (uint)ParseUnsignedRational((ulong)val);
                }
                else
                {
                    return -1;// String.Empty;
                }
            }
        }

        public long FocalLength
        {
            get
            {
                object val = QueryMetadata("/app1/ifd/exif/subifd:{uint=37386}");
                if (val != null)
                {
                    return (uint)ParseUnsignedRational((ulong)val);// String.Format("{0}mm", ParseUnsignedRational((ulong)val));
                }
                else
                {
                    return -1;// String.Empty;
                }



            }
        }

        public long IsoSpeed
        {
            get
            {
                object val = QueryMetadata("/app1/ifd/exif/subifd:{uint=34855}");
                if (val != null)
                {
                    return (ushort)val;
                }
                else
                {
                    return -1;
                }



            }
        }

   
        public int FlashMode
        {
            get
            {
                object val = QueryMetadata("/app1/ifd/exif/subifd:{uint=37385}");
                if (val != null)
                {

                    if (((ushort)val % 2) == 1)
                        return 1;//FlashMode.FlashFired;
                    else
                        return 0;// FlashMode.FlashDidNotFire;
                }
                else
                {
                    return -1;
                }
            }
        }

        public int ExposureMode
        {
            get
            {
                ushort? mode = (ushort?)QueryMetadata("/app1/ifd/exif/subifd:{uint=34850}");

                if (mode == null)
                {
                    return -1;
                }
                else
                {

                    return (int)mode;
                }
            }
        }



        public int WhiteBalanceMode
        {
            get
            {
                ushort? mode = (ushort?)QueryMetadata("/app1/ifd/exif/subifd:{uint=37384}");

                if (mode == null)
                {
                    return -1;
                }
                else
                {
                    return (int)mode;

                }
            }
        }


        public string DateTaken
        {
            get
            {


                string val = null;
                try
                {
                    val = _metadata.DateTaken;
                }
                catch (Exception ex)
                {
                    Trace.WriteLine("Error getting MetaData, DateTaken: " + ex.ToString());
                }
                return (val != null ? (string)val : String.Empty);

            }

        }
    }

}